package org.ucas.kgraph.utils.common;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

public abstract class OrdinalEnumUserType<E extends Enum<E>> implements UserType,ParameterizedType {

  protected Class<E> clazz;

  protected Class<Enum<?>> enumClass;

  protected Object enumObject;

  protected Method getPersistedValue;

  protected Method returnEnum;


  protected OrdinalEnumUserType(Class<E> clazz) {
    this.clazz = clazz;
  }

  private static final int[] SQL_TYPES = {Types.TINYINT};
  public int[] sqlTypes() {
    return SQL_TYPES;
  }

  public Class<?> returnedClass() {
    return clazz;
  }

  @Override
  public void setParameterValues(Properties parameters) {
    if (parameters != null) {
      try {
        enumClass = (Class<Enum<?>>) Class.forName(parameters.get("enumClass").toString());
        enumObject = enumClass.getEnumConstants()[0];
        getPersistedValue = enumClass.getMethod("getPersistedValue");
        returnEnum = enumClass.getMethod("returnEnum",
                new Class[] { String.class });
      } catch (SecurityException e) {
        e.printStackTrace();
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      }
    }
  }

  @Override
  public Object nullSafeGet(ResultSet resultSet, String[] strings, SessionImplementor sessionImplementor, Object o) throws HibernateException, SQLException {
    String value = resultSet.getString(strings[0]);
    Object returnVal = null;

    if (value == null)
      return null;
    else {
      try {
        returnVal = returnEnum
                .invoke(enumObject, new Object[] { value });
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
    }
    return returnVal;
  }

  @Override
  public void nullSafeSet(PreparedStatement preparedStatement, Object o, int i, SessionImplementor sessionImplementor) throws HibernateException, SQLException {
    if (null == o) {
      preparedStatement.setNull(i, Types.NUMERIC);
    } else {
      //Hibernate.String.nullSafeSet(st, sex, index);
      try {
        preparedStatement.setString(i,  getPersistedValue.invoke(o).toString());
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
    }
  }



  public Object deepCopy(Object value) throws HibernateException{
    return value;
  }

  public boolean isMutable() {
    return false;
  }

  public Object assemble(Serializable cached, Object owner)
          throws HibernateException {
    return deepCopy(cached);
  }

  public Serializable disassemble(Object value) throws HibernateException {
    return (Serializable)value;
  }

  public Object replace(Object original, Object target, Object owner)
          throws HibernateException {
    return original;
  }
  public int hashCode(Object x) throws HibernateException {
    return x.hashCode();
  }
  public boolean equals(Object x, Object y) throws HibernateException {
    if (x == y)
      return true;
    if (null == x || null == y)
      return false;
    return x.equals(y);
  }
}